use std::sync::Arc;
use tauri::Window;
use tokio::{net::TcpStream, time::timeout, io::AsyncReadExt, sync::Semaphore};
use crate::{error::{AppErr, ErrorHandler, static_err}, robot::{STATE_ADDR, CONNECT_DURATION, state_proto::{le, check_sum}, STATE_EVENT}, utils::new_bytes};
use super::state_proto::{StateResp, parse_proto, MIN_SIZE};

pub struct StateConn {

    exit_sem: Arc<Semaphore>,
}


impl StateConn {

    pub fn exit(&self) {
        self.exit_sem.add_permits(1);
    }

    pub async fn connect(window: Window) -> Result<Self,AppErr> {

        let stream = TcpStream::connect(STATE_ADDR);
        let stream = timeout(CONNECT_DURATION, stream)
        .await
        .map_app("STATE连接超时")??;
        
        let sem = Arc::new( Semaphore::new(0) );

        let conn = StateConn {
            exit_sem: sem.clone(),
        };

        tokio::spawn( read_loop(sem, stream, window) );

        Ok(conn)
    }
}

async fn read_loop(exit_sem: Arc<Semaphore>, mut stream: TcpStream, window: Window) {

    loop {

        tokio::select! {
            _ = exit_sem.acquire() => {
                break;
            }

            resp = read_state(&mut stream) => {
                match resp {
                    Ok(resp) => {
                        let _ = window.emit(STATE_EVENT, resp);
                    },

                    Err(e) => {
                        println!("state exit err:{}", e);
                        break;
                    }
                };
            }
        }
    }

    println!("state exit");
}

async fn read_state_buf(stream: &mut TcpStream) -> Result<Box<[u8]>, AppErr> {

    let mut buf: [u8; 5] = [0; 5];
    stream.read_exact(&mut buf).await?;

    let head = le::decode_u16(&buf);
    if head != 0x5A5A {
        return static_err("STATE head != 0x5A5A");
    }

    let next_len = le::decode_u16(&buf[3..]) as usize;
    let mut body = new_bytes(next_len + 2);
    stream.read_exact(&mut body).await?;

    let s = check_sum(&buf) + check_sum(&body[..next_len]);
    let sum = le::decode_u16(&body[next_len..]);
    if s != sum {
        return static_err("STATE CHECK SUM fail");
    }

    Ok(body)
}

async fn read_state(stream: &mut TcpStream) -> Result<StateResp, AppErr> {

    let buf = read_state_buf(stream).await?;
    if buf.len() < MIN_SIZE {
        return static_err("STATE LEN < MIN_SIZE");
    }
    let resp = parse_proto(&buf);
    Ok(resp)
}

